<?php

declare(strict_types=1);
/**
 * Author:  Zane
 * Email: 873934580@qq.com
 * Date: 2021/12/7.
 */

namespace App\Util;

use App\Exceptions\CommonExceptionConstants;
use App\Exceptions\ServiceException;
use Illuminate\Database\Eloquent\Model;

class QueryBuildUtil
{
    private $filterMap;

    public static function build($filterMap, array $search, array $allow = [])
    {
        return (new static())->queryBuild($filterMap, $search, $allow);
    }

    /**
     * 构建模型查询参数 - 通过数组的方式通用的构建简单的查询条件.
     *
     * @param Model 查询模型       $filterMap
     * @param array 参数             $search
     * @param array 允许查询参数 $allow
     */
    public function queryBuild($filterMap, array $search, array $allow = [])
    {
        $this->filterMap = $filterMap;
        if (0 === \count($allow)) {
            return $this->filterMap;
        }
        foreach ($search as $key => $val) {
            if (null === $val || '' === $val) {
                continue;
            }
            if (!isset($allow[$key])) {
                continue;
            }
            $val = $this->filter($val, $allow[$key]);
            $this->where($key, $val, $allow[$key]);
        }

        return $this->filterMap;
    }

    /**
     * 参数过滤操作.
     *
     * @param $val
     * @param $allow
     *
     * @return string
     */
    private function filter($val, $allow): string
    {
        $val = trim($val);
        if (isset($allow['fun'])) {
            if ($allow['fun'] instanceof \Closure) {
                return $allow['fun']($val);
            }
            if (!method_exists($this, $allow['fun'])) {
                throw new ServiceException(CommonExceptionConstants::ERROR_PARAM);
            }
            $val = $this->{$allow['fun']}($val);
        }

        return $val;
    }

    /**
     * 条件构造.
     *
     * @param $val
     * @param $allow
     * @param $name
     */
    private function where($name, $val, $allow): void
    {
        $symbol = '=';
        if (isset($allow['symbol'])) {
            $symbol = strtoupper($allow['symbol']);
            if (method_exists($this, $symbol)) {
                $this->{$symbol}($name, $val);

                return;
            }
        }

        $this->filterMap->where($name, $symbol, $val);
    }

    private function EQ($name, $val): void
    {
        $this->filterMap->where($name, $val);
    }

    private function GT($name, $val): void
    {
        $this->filterMap->where($name, '>', $val);
    }

    private function NE($name, $val): void
    {
        $this->filterMap->where($name, '<>', $val);
    }

    private function LT($name, $val): void
    {
        $this->filterMap->where($name, '<', $val);
    }

    private function GE($name, $val): void
    {
        $this->filterMap->where($name, '>=', $val);
    }

    private function LE($name, $val): void
    {
        $this->filterMap->where($name, '<=', $val);
    }

    private function LIKE($name, $val): void
    {
        $this->filterMap->where($name, 'like', '%'.$val.'%');
    }

    private function IN($name, $val): void
    {
        if (\is_string($val)) {
            $val = explode(',', $val);
        }
        if (\is_array($val)) {
            $this->filterMap->whereIn($name, $val);
        }
    }

    private function NOT_IN($name, $val): void
    {
        if (\is_string($val)) {
            $val = explode(',', $val);
        }
        if (\is_array($val)) {
            $this->filterMap->whereNotIn($name, $val);
        }
    }

    private function BETWEEN($name, $val): void
    {
        if (\is_string($val)) {
            $val = explode(',', $val);
        }
        if (\is_array($val)) {
            $this->filterMap->whereBetween($name, [reset($val), end($val)]);
        }
    }
}
